library(MAST)
Loading required package: SummarizedExperiment
Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
Loading required package: DelayedArray
Loading required package: matrixStats

Attaching package: ‘matrixStats’

The following objects are masked from ‘package:Biobase’:

    anyMissing, rowMedians


Attaching package: ‘DelayedArray’

The following objects are masked from ‘package:matrixStats’:

    colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges

The following object is masked from ‘package:base’:

    apply


Attaching package: ‘MAST’

The following object is masked from ‘package:stats’:

    filter

Simple PCAs to begin

for(i in 1:length(data)){
  y <- data[[i]]$human
  pca <- prcomp(t(y[order(-1*apply(y,1,var)),][1:1000,]),scale=T)
  pca <- cbind(data[[i]]$meta,CDR=colSums(y>0),pca$x)
  print(ggpairs(pca, columns=c('PC1', 'PC2', 'PC3', 'PC4', 'CDR'),
          mapping=aes(color=Treatment), upper=list(continuous='cor')))   
}

 plot: [1,1] [====----------------------------------------------------------------------------------------------]  4% est: 0s 
 plot: [1,2] [========------------------------------------------------------------------------------------------]  8% est: 5s 
 plot: [1,3] [============--------------------------------------------------------------------------------------] 12% est: 6s 
 plot: [1,4] [================----------------------------------------------------------------------------------] 16% est: 6s 
 plot: [1,5] [====================------------------------------------------------------------------------------] 20% est: 7s 
 plot: [2,1] [========================--------------------------------------------------------------------------] 24% est: 7s 
 plot: [2,2] [===========================-----------------------------------------------------------------------] 28% est: 6s 
 plot: [2,3] [===============================-------------------------------------------------------------------] 32% est: 6s 
 plot: [2,4] [===================================---------------------------------------------------------------] 36% est: 5s 
 plot: [2,5] [=======================================-----------------------------------------------------------] 40% est: 6s 
 plot: [3,1] [===========================================-------------------------------------------------------] 44% est: 6s 
 plot: [3,2] [===============================================---------------------------------------------------] 48% est: 5s 
 plot: [3,3] [===================================================-----------------------------------------------] 52% est: 4s 
 plot: [3,4] [=======================================================-------------------------------------------] 56% est: 4s 
 plot: [3,5] [===========================================================---------------------------------------] 60% est: 4s 
 plot: [4,1] [===============================================================-----------------------------------] 64% est: 3s 
 plot: [4,2] [===================================================================-------------------------------] 68% est: 3s 
 plot: [4,3] [=======================================================================---------------------------] 72% est: 2s 
 plot: [4,4] [==========================================================================------------------------] 76% est: 2s 
 plot: [4,5] [==============================================================================--------------------] 80% est: 2s 
 plot: [5,1] [==================================================================================----------------] 84% est: 1s 
 plot: [5,2] [======================================================================================------------] 88% est: 1s 
 plot: [5,3] [==========================================================================================--------] 92% est: 1s 
 plot: [5,4] [==============================================================================================----] 96% est: 0s 
 plot: [5,5] [==================================================================================================]100% est: 0s 
                                                                                                                              

 plot: [1,1] [====----------------------------------------------------------------------------------------------]  4% est: 0s 
 plot: [1,2] [========------------------------------------------------------------------------------------------]  8% est: 5s 
 plot: [1,3] [============--------------------------------------------------------------------------------------] 12% est: 5s 
 plot: [1,4] [================----------------------------------------------------------------------------------] 16% est: 5s 
 plot: [1,5] [====================------------------------------------------------------------------------------] 20% est: 6s 
 plot: [2,1] [========================--------------------------------------------------------------------------] 24% est: 6s 
 plot: [2,2] [===========================-----------------------------------------------------------------------] 28% est: 5s 
 plot: [2,3] [===============================-------------------------------------------------------------------] 32% est: 5s 
 plot: [2,4] [===================================---------------------------------------------------------------] 36% est: 4s 
 plot: [2,5] [=======================================-----------------------------------------------------------] 40% est: 4s 
 plot: [3,1] [===========================================-------------------------------------------------------] 44% est: 4s 
 plot: [3,2] [===============================================---------------------------------------------------] 48% est: 4s 
 plot: [3,3] [===================================================-----------------------------------------------] 52% est: 4s 
 plot: [3,4] [=======================================================-------------------------------------------] 56% est: 3s 
 plot: [3,5] [===========================================================---------------------------------------] 60% est: 3s 
 plot: [4,1] [===============================================================-----------------------------------] 64% est: 3s 
 plot: [4,2] [===================================================================-------------------------------] 68% est: 2s 
 plot: [4,3] [=======================================================================---------------------------] 72% est: 2s 
 plot: [4,4] [==========================================================================------------------------] 76% est: 2s 
 plot: [4,5] [==============================================================================--------------------] 80% est: 1s 
 plot: [5,1] [==================================================================================----------------] 84% est: 1s 
 plot: [5,2] [======================================================================================------------] 88% est: 1s 
 plot: [5,3] [==========================================================================================--------] 92% est: 1s 
 plot: [5,4] [==============================================================================================----] 96% est: 0s 
 plot: [5,5] [==================================================================================================]100% est: 0s 
                                                                                                                              

 plot: [1,1] [====----------------------------------------------------------------------------------------------]  4% est: 0s 
 plot: [1,2] [========------------------------------------------------------------------------------------------]  8% est: 3s 
 plot: [1,3] [============--------------------------------------------------------------------------------------] 12% est: 3s 
 plot: [1,4] [================----------------------------------------------------------------------------------] 16% est: 3s 
 plot: [1,5] [====================------------------------------------------------------------------------------] 20% est: 3s 
 plot: [2,1] [========================--------------------------------------------------------------------------] 24% est: 3s 
 plot: [2,2] [===========================-----------------------------------------------------------------------] 28% est: 3s 
 plot: [2,3] [===============================-------------------------------------------------------------------] 32% est: 3s 
 plot: [2,4] [===================================---------------------------------------------------------------] 36% est: 3s 
 plot: [2,5] [=======================================-----------------------------------------------------------] 40% est: 3s 
 plot: [3,1] [===========================================-------------------------------------------------------] 44% est: 2s 
 plot: [3,2] [===============================================---------------------------------------------------] 48% est: 2s 
 plot: [3,3] [===================================================-----------------------------------------------] 52% est: 2s 
 plot: [3,4] [=======================================================-------------------------------------------] 56% est: 2s 
 plot: [3,5] [===========================================================---------------------------------------] 60% est: 2s 
 plot: [4,1] [===============================================================-----------------------------------] 64% est: 2s 
 plot: [4,2] [===================================================================-------------------------------] 68% est: 2s 
 plot: [4,3] [=======================================================================---------------------------] 72% est: 1s 
 plot: [4,4] [==========================================================================------------------------] 76% est: 1s 
 plot: [4,5] [==============================================================================--------------------] 80% est: 1s 
 plot: [5,1] [==================================================================================----------------] 84% est: 1s 
 plot: [5,2] [======================================================================================------------] 88% est: 1s 
 plot: [5,3] [==========================================================================================--------] 92% est: 0s 
 plot: [5,4] [==============================================================================================----] 96% est: 0s 
 plot: [5,5] [==================================================================================================]100% est: 0s 
                                                                                                                              

The CDR clearly correlates strongle to the either or both the 1st and/or 2nd principal components (PCs). This makes it clear that the CDR should always be considered when performing any modelling. Let’s us see the propotion of variance accounted for by these first PCs.

lapply(data,function(x){
  y <- x$human
  pca <- prcomp(t(y[order(-1*apply(y,1,var)),][1:1000,]),scale=T)
  eigs <- pca$sdev^2
  rbind("Prop. of Var."=eigs[1:5] / sum(eigs),"Cum. Sum. Prop. of Var."=cumsum(eigs[1:5])/sum(eigs))
})
$VHI098
                              [,1]       [,2]       [,3]       [,4]       [,5]
Prop. of Var.           0.08551196 0.03397286 0.02837749 0.01915273 0.01623797
Cum. Sum. Prop. of Var. 0.08551196 0.11948482 0.14786231 0.16701503 0.18325300

$VHI098.resist
                              [,1]       [,2]      [,3]       [,4]      [,5]
Prop. of Var.           0.05854842 0.04428817 0.0273953 0.02453004 0.0207859
Cum. Sum. Prop. of Var. 0.05854842 0.10283658 0.1302319 0.15476192 0.1755478

$HCI009
                             [,1]       [,2]       [,3]       [,4]       [,5]
Prop. of Var.           0.1145667 0.04506263 0.02884657 0.01993381 0.01282113
Cum. Sum. Prop. of Var. 0.1145667 0.15962932 0.18847589 0.20840970 0.22123083
for(i in 1:length(data)){
  y <- cbind(data[[i]]$meta,CDR=colSums(data[[i]]$human > 0))
  if("Response" %in% names(y)){
    print(ggplot(y) + aes(x=Response,y=CDR,fill=Response,group=Sample) + geom_violin(draw_quantiles = c(.25,.5,.75)))
  } else {
    print(ggplot(y) + aes(x=Treatment,y=CDR,fill=Treatment,group=Sample) + geom_violin(draw_quantiles = c(.25,.5,.75)))
  }
}

It is clear that the CDR has been driving a lot of our observations. Whether this is biological is seperate question, but regardless we should include it in our modelling attempts. Let us perform the DE analysis using edgeR, as done by Mike, but this time including the CDR.

DE Analysis

Niave Model (no CDR)

This is the orginal model used by Mike. For this initial test, we will only use the VHIO098 dataset. For reference, Mike’s analysis returned:

TreatmentTreated vs. TreatmentUntreated -1 1398 0 9854 1 2210

design.mat <- model.matrix(~ 0 + Treatment + Sample, data=data$VHI098$meta)
contrast.mat <-  makeContrasts(TreatmentTreated-TreatmentUntreated, levels=design.mat)
vfit <- lmFit(data$VHI098$human, design.mat)
Coefficients not estimable: SampleSIGAH11 
Partial NA coefficients for 13462 probe(s)
vfit <- contrasts.fit(vfit, contrasts=contrast.mat)
efit <- eBayes(vfit)
DE <- as.data.table(topTable(efit,p.value = 0.05, n=Inf),keep.rownames=TRUE)
DE[,direction:=factor(logFC>0)]
levels(DE$direction) <- c("down.reg","up.reg")
models <- list()
models$niave <- DE
DE[,.N,direction]

As expected, this is the same as what Mike obtained.

Niave model with CDR

Let’s first look at the relative AICs when adding this factor.

aic <- with(cbind(data$VHI098$meta,CDR=colSums(data$VHI098$human>0)),
            selectModel(data$VHI098$human, list(
              model.matrix(~ 0 + Treatment + Sample),
              model.matrix(~ 0 + Treatment + Sample + CDR)
            ), criterion="aic"))
Coefficients not estimable: SampleSIGAH11 
Partial NA coefficients for 13462 probe(s)
Coefficients not estimable: SampleSIGAH11 
Partial NA coefficients for 13462 probe(s)
table(aic$pref)

    1     2 
 2853 10609 
table(exp(as.numeric(diff(t(aic$IC[aic$pref=="2",])))/2) < 0.05) #FDR correction needed?

FALSE  TRUE 
 2281  8328 

The AIC shows that the majority of genes are fitted better by the model that incorporates the CDR. Furthermore, the propabilities that this model is an improvement for this “subset” leaves no doubts as to using it.

design.mat <- model.matrix(~ 0 + Treatment + Sample + CDR,
                           data=cbind(data$VHI098$meta,CDR=colSums(data$VHI098$human>0)))
contrast.mat <-  makeContrasts(TreatmentTreated-TreatmentUntreated, levels=design.mat)
vfit <- lmFit(data$VHI098$human, design.mat)
Coefficients not estimable: SampleSIGAH11 
Partial NA coefficients for 13462 probe(s)
vfit <- contrasts.fit(vfit, contrasts=contrast.mat)
efit <- eBayes(vfit)
DE <- as.data.table(topTable(efit,p.value = 0.05, n=Inf),keep.rownames=TRUE)
DE[,direction:=factor(logFC>0)]
levels(DE$direction) <- c("down.reg","up.reg")
models$niave.CDR <- DE
DE[,.N,direction]

We see a large reduction in the number of up.reg genes which is not quite matched matched by an increase in the number of down.reg genes. In total, the extra coeffienct reduces the total number of DE genes by roughly 600. This is a good indication that it should be included in the model, as the number of samples is such that are we are fairly safe from overfitting. What is the overlap between these models?

x <- merge(models$niave[,.(rn,ID,niave=direction)],
      models$niave.CDR[,.(rn,ID,niave.CDR=direction)],
      by=c("rn","ID"),all=TRUE,)
levels(x$niave) <- c(-1,1,0)
levels(x$niave.CDR) <- c(-1,1,0)
x[is.na(x)] <- 0
vennDiagram(x[,.(niave,niave.CDR)],cex=0.8,include="up",main="Up.reg.DE")

vennDiagram(x[,.(niave,niave.CDR)],cex=0.8,include="down",main="Down.reg.DE")

Logistic Model

For this model, let us model the expression as either on (!=0) or off (==0). Then let’s use logistic regression to model the treatment effect.

data.binary <- as.data.table(data$VHI098$human != 0)
data.binary[,row.id:=.I]
DE <- with(data$VHI098$meta,
     data.binary[, as.list(summary(glm(unlist(.SD)~Treatment+Sample+cdr))$coefficients[2,]), row.id]
     )
DE$row.id <- rownames(data$VHI098$human)
DE[,fdr:=p.adjust(`Pr(>|t|)`, 'fdr')]
DE <- DE[fdr<0.05]
DE[,direction:=factor(Estimate>0)]
levels(DE$direction) <- c("down.reg","up.reg")
models$logistic <- DE
DE[,.N,direction]

Zero-inflated model

We need to first determine an appropiate threshold to below which we set the value to zero.

thres <- thresholdSCRNACountMatrix(data$VHI098$human, nbins = 20, min_per_bin = 30)
(0.427,0.729] (0.729,0.903]  (0.903,1.09]   (1.09,1.31]   (1.31,1.54]   (1.54,1.79]   (1.79,2.08]   (2.08,2.39]   (2.39,2.73]    (2.73,3.1]    (3.1,3.52]   (3.52,3.97]   (3.97,4.47]   (4.47,5.02] 
            0             0             0             0             0             0             0             0             0             0             0             0             0             0 
   (5.02,6.3]    (6.3,8.73] 
            0             0 
par(mfrow=c(5,4))
print(plot(thres))
$ask
[1] FALSE

Mike’s normalisation has apparently already done this and at all median expression binnings, the threshold is reported as zero.

expMat <- as.matrix(data$VHI098$human)
rownames(expMat) <- make.names(rownames(expMat),unique = T)
cData <- as.data.frame(cbind(data$VHI098$meta,CDR=colSums(data$VHI098$human>0)))
rownames(cData) <- colnames(expMat)
cData$Treatment <- factor(cData$Treatment,levels = c("Untreated","Treated"))
fData <- data.frame(gene=rownames(expMat))
rownames(fData) <- rownames(expMat)
x <- FromMatrix(expMat,cData,fData)
`fData` has no primerid.  I'll make something up.
`cData` has no wellKey.  I'll make something up.
y <- zlm(~ Treatment + Sample + CDR, x)
Coefficients SampleSIGAH11 are never estimible and will be dropped..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Done!
z <- summary(y,doLRT="TreatmentTreated")
Combining coefficients and standard errors
Calculating log-fold changes
Calculating likelihood ratio tests
Refitting on reduced model...
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Done!
z <- z$datatable
DE <- merge(
  z[contrast=='TreatmentTreated' & component=='H',.(primerid, `Pr(>Chisq)`)],
  z[contrast=='TreatmentTreated' & component=='logFC', .(primerid, coef, ci.hi, ci.lo)],
by='primerid')
DE[,fdr:=p.adjust(`Pr(>Chisq)`, 'fdr')]
DE <- DE[fdr<0.05]
DE[,direction:=factor(coef>0)]
levels(DE$direction) <- c("down.reg","up.reg")
models$zero.inf <- DE
DE[,.N,direction]

Note that the above model calls something as DE if the hurdle (gene being on or off) is signifgantly altered w.r.t. to the contrast tested. There does not have to be a change in the expression of genes that are “on”.

What is the overlap w.r.t. the other models?

x <- merge(models$niave[,.(rn,ID,niave=direction)],
      models$niave.CDR[,.(rn,ID,niave.CDR=direction)],
      by=c("rn","ID"),all=TRUE,)
x <- merge(x,
           models$zero.inf[,.(primerid,zero.inf=direction)],
           by.x=c("ID"),by.y=c("primerid"),all=TRUE)
x <- merge(x,
           models$logistic[,.(row.id,logistic=direction)],
           by.x=c("ID"),by.y=c("row.id"),all=TRUE)
levels(x$niave) <- c(-1,1,0)
levels(x$niave.CDR) <- c(-1,1,0)
levels(x$zero.inf) <- c(-1,1,0)
levels(x$logistic) <- c(-1,1,0)
x[is.na(x)] <- 0
vennDiagram(x[,.(niave,niave.CDR,zero.inf,logistic)],cex=0.8,include="up",main="Up.reg.DE")

vennDiagram(x[,.(niave,niave.CDR,zero.inf,logistic)],cex=0.8,include="down",main="Down.reg.DE")

And let’s see what happends when we enforce a LFC threshold.

x <- merge(models$niave[abs(logFC)>.5,.(rn,ID,niave=direction)],
      models$niave.CDR[abs(logFC)>.5,.(rn,ID,niave.CDR=direction)],
      by=c("rn","ID"),all=TRUE,)
x <- merge(x,
           models$zero.inf[abs(coef)>.5,.(primerid,zero.inf=direction)],
           by.x=c("ID"),by.y=c("primerid"),all=TRUE)
levels(x$niave) <- c(-1,1,0)
levels(x$niave.CDR) <- c(-1,1,0)
levels(x$zero.inf) <- c(-1,1,0)
x[is.na(x)] <- 0
vennDiagram(x[,.(niave,niave.CDR,zero.inf)],cex=0.8,include="up",main="Up.reg.DE")

vennDiagram(x[,.(niave,niave.CDR,zero.inf)],cex=0.8,include="down",main="Down.reg.DE")

DE genes plots

Finally, let’s make a few pretty plots of the most DE genes for each model.

Niave model

DE <- models$niave
i <- which(rownames(data$VHI098$human) %in% DE[order(adj.P.Val)][abs(logFC)>.5][1:16,ID])
x <- merge(melt(data$VHI098$human[i,]),data$VHI098$meta,by.x=c("Var2"),by.y=c("sub.sample"))
ggplot(x) + aes(x=Treatment,y=value,group=Sample,fill=Treatment) + geom_violin(draw_quantiles = c(.5)) + facet_wrap(~Var1,scale='free_y')

DE <- models$niave
i <- which(rownames(data$VHI098$human) %in% DE[order(adj.P.Val)][abs(logFC)>.5][1:50,ID])
j <- data$VHI098$meta[,order(Treatment)]
labels.col <- rep("",length(i))
ann.col <- as.data.frame(data$VHI098$meta[j,.(Sample,Treatment)])
rownames(ann.col) <- data$VHI098$meta[j,sub.sample]
pheatmap(data$VHI098$human[i,j],labels_col=labels.col,annotation_col = ann.col,cluster_cols = F)

Niave CDR model

DE <- models$niave.CDR
i <- which(rownames(data$VHI098$human) %in% DE[order(adj.P.Val)][abs(logFC)>.5][1:16,ID])
x <- merge(melt(data$VHI098$human[i,]),data$VHI098$meta,by.x=c("Var2"),by.y=c("sub.sample"))
ggplot(x) + aes(x=Treatment,y=value,group=Sample,fill=Treatment) + geom_violin(draw_quantiles = c(.5)) + facet_wrap(~Var1,scale='free_y')

DE <- models$niave.CDR
i <- which(rownames(data$VHI098$human) %in% DE[order(adj.P.Val)][abs(logFC)>.5][1:50,ID])
j <- data$VHI098$meta[,order(Treatment)]
labels.col <- rep("",length(i))
ann.col <- as.data.frame(data$VHI098$meta[j,.(Sample,Treatment)])
rownames(ann.col) <- data$VHI098$meta[j,sub.sample]
pheatmap(data$VHI098$human[i,j],labels_col=labels.col,annotation_col = ann.col,cluster_cols = F)

Zero-inflated model

DE <- models$zero.inf
i <- which(rownames(data$VHI098$human) %in% DE[order(fdr)][abs(coef)>.5][1:16,primerid])
x <- merge(melt(data$VHI098$human[i,]),data$VHI098$meta,by.x=c("Var2"),by.y=c("sub.sample"))
ggplot(x) + aes(x=Treatment,y=value,group=Sample,fill=Treatment) + geom_violin(draw_quantiles = c(.5)) + facet_wrap(~Var1,scale='free_y')

DE <- models$zero.inf
i <- which(rownames(data$VHI098$human) %in% DE[order(fdr)][abs(coef)>.5][1:50,primerid])
j <- data$VHI098$meta[,order(Treatment)]
labels.col <- rep("",length(i))
ann.col <- as.data.frame(data$VHI098$meta[j,.(Sample,Treatment)])
rownames(ann.col) <- data$VHI098$meta[j,sub.sample]
pheatmap(data$VHI098$human[i,j],labels_col=labels.col,annotation_col = ann.col,cluster_cols = F)

LS0tCnRpdGxlOiAnUERYOiBEcnVnIHRyYWlscycKYXV0aG9yOiAiQWxpc3RhaXIgTWFydGluIgpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICJMYXN0IG1vZGlmaWVkOiAlZCAlYiAlWSIpYCcKb3V0cHV0OgogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKc3VidGl0bGU6IHNjUk5Bc2VxIFNlbHUgVHJhaWwKbGF5b3V0OiBwYWdlCi0tLQoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpyb290LmRpciA8LSAifi9PbmVEcml2ZS9wcm9qZWN0cy8iCnNvdXJjZShwYXN0ZTAocm9vdC5kaXIsIlIudXRpbHMvUk5BU0VRLnV0aWxzLlIiKSkKcHJvamVjdC5kaXIgPC0gcGFzdGUwKHJvb3QuZGlyLCJQRFgvIikKZm5hbWUgPC0gcGFzdGUwKHByb2plY3QuZGlyLCJkYXRhL1JkYXRhLmR1bXBzL3NjUk5Bc2VxLnNlbHUuUmRhdGEiKQpsaWJyYXJ5KE1BU1QpCgppZihmaWxlLmV4aXN0cyhmbmFtZSkpewogIGxvYWQoZm5hbWUpCn0gZWxzZSB7CiAgc2F2ZShkYXRhLGZpbGU9cGFzdGUwKHByb2plY3QuZGlyLCJyZXN1bHRzL3NjUk5Bc2VxLnNoaW55Lmhpc3QvZGF0YS9zY1JOQXNlcS5wcm9jZXNzZWQuUmRhdGEiKSkKICAKICBoZzE5LmVuc2VtYmwgPC0gdXNlRW5zZW1ibChiaW9tYXJ0PSdlbnNlbWJsJywgZGF0YXNldD0naHNhcGllbnNfZ2VuZV9lbnNlbWJsJywgR1JDaD0zNykKICAKICBkYXRhIDwtIGxpc3QoKQogIGRhdGEkVkhJMDk4IDwtIGZyZWFkKHBhc3RlMChwcm9qZWN0LmRpciwiZGF0YS9zY1JOQXNlcS9BTExfaGcxOS1TRm5vcm0udHN2IikpCiAgZGF0YSRWSEkwOTgucmVzaXN0IDwtIGZyZWFkKHBhc3RlMChwcm9qZWN0LmRpciwiZGF0YS9zY1JOQXNlcS9WSElPOThfcmVzaXN0LVNGbm9ybS50c3YiKSkKICBkYXRhJEhDSTAwOSA8LSBmcmVhZChwYXN0ZTAocHJvamVjdC5kaXIsImRhdGEvc2NSTkFzZXEvSENJMDA5LVNGbm9ybS50c3YiKSkKICBkYXRhc2V0cyA8LSBuYW1lcyhkYXRhKQogIAogIGRhdGEgPC0gbGFwcGx5KGRhdGFzZXRzLGZ1bmN0aW9uKGRhdGFzZXQpewogICAgeCA8LSBkYXRhW1tkYXRhc2V0XV0KICAgIGdlbmVfaWRzIDwtIHhbLGdzdWIoZ2VuZV9pZCwgcGF0dGVybj0iaGcxOV8iLCByZXBsYWNlbWVudD0iIildCiAgICBnZW5lX3N5bWJvbHMgPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCdlbnNlbWJsX2dlbmVfaWQnLCAnZXh0ZXJuYWxfZ2VuZV9uYW1lJyksZmlsdGVycz0nZW5zZW1ibF9nZW5lX2lkJywgbWFydD1oZzE5LmVuc2VtYmwsdmFsdWVzPWdlbmVfaWRzKQogIAogICAgaWYoZGF0YXNldD09IlZISTA5OCIpewogICAgICBtZXRhLmRhdGEgPC0gZGF0YS50YWJsZShTYW1wbGU9YygiU0lHQUExMSIsICJTSUdBQzExIiwgIlNJR0FEMTEiLCAiU0lHQUUxMSIsICJTSUdBRjExIiwgIlNJR0FHMTEiLCAiU0lHQUgxMSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmVhdG1lbnQ9YygiVHJlYXRlZCIsICJUcmVhdGVkIiwgIlRyZWF0ZWQiLCAiVW50cmVhdGVkIiwgIlRyZWF0ZWQiLCAiVW50cmVhdGVkIiwgIlVudHJlYXRlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNwb25zZT1jKCJSZXNwb25kZXIiLCAiUmVzaXN0YW50IiwgIlJlc2lzdGFudCIsICJVbnRyZWF0ZWQiLCAiUmVzcG9uZGVyIiwgIlVudHJlYXRlZCIsICJVbnRyZWF0ZWQiKSkKICAgIH0gZWxzZSBpZihkYXRhc2V0PT0iVkhJMDk4LnJlc2lzdCIpewogICAgICBtZXRhLmRhdGEgPC0gZGF0YS50YWJsZShTYW1wbGU9YygiU0lHQUE4IiwgIlNJR0FCOCIsICJTSUdBQzgiLCAiU0lHQUQ4IiwgIlNJR0FFOCIsICJTSUdBRzgiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50PWMoIlRyZWF0ZWQiLCAiVW50cmVhdGVkIiwgIlRyZWF0ZWQiLCAiVW50cmVhdGVkIiwgIlRyZWF0ZWQiLCAiVW50cmVhdGVkIikpCiAgICB9IGVsc2UgaWYoZGF0YXNldD09IkhDSTAwOSIpewogICAgICBtZXRhLmRhdGEgPC0gZGF0YS50YWJsZShTYW1wbGU9YygiU0lHQUE3IiwgIlNJR0FCNyIsICJTSUdBQzciLCAiU0lHQUQ3IiwgIlNJR0FFNyIsICJTSUdBRjciLCAiU0lHQUc3IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyZWF0bWVudD1jKCJUcmVhdGVkIiwgIlVudHJlYXRlZCIsICJVbnRyZWF0ZWQiLCAiVW50cmVhdGVkIiwgIlVudHJlYXRlZCIsICJUcmVhdGVkIiwgIlRyZWF0ZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVzcG9uc2U9YygiUmVzcG9uZGVyIiwgIlVudHJlYXRlZCIsICJVbnRyZWF0ZWQiLCAiVW50cmVhdGVkIiwgIlVudHJlYXRlZCIsICJSZXNwb25kZXIiLCAiUmVzaXN0YW50IikpCiAgICB9IGVsc2UgeyAKICAgICAgc3RvcCgiZGF0YXNldCBub3QgcmVjb2duaXNlZCIpCiAgICB9CiAgICB1YmVyLm1ldGEgPC0gZGF0YS50YWJsZShzdWIuc2FtcGxlPWhlYWQoY29sbmFtZXMoeCksLTEpKQogICAgdWJlci5tZXRhWyxTYW1wbGU6PXRzdHJzcGxpdChzdWIuc2FtcGxlLCJfIilbMV1dCiAgICB1YmVyLm1ldGEgPC0gbWVyZ2UodWJlci5tZXRhLCBtZXRhLmRhdGEpCiAgICAKICAgIHkgPC0gbGlzdCgpCiAgICB5JG1ldGEgPC0gdWJlci5tZXRhCiAgICB5JGh1bWFuIDwtIGFzLm1hdHJpeCh4WywgMTooZGltKHgpWzJdLTEpXSkKICAgIHJvd25hbWVzKHkkaHVtYW4pIDwtIGdlbmVfc3ltYm9scyRleHRlcm5hbF9nZW5lX25hbWUKICAgIHkKICB9KQogIG5hbWVzKGRhdGEpIDwtIGRhdGFzZXRzCiAgCiAgc2F2ZShkYXRhLGZpbGU9cGFzdGUwKHByb2plY3QuZGlyLCJyZXN1bHRzL3NjUk5Bc2VxLnNoaW55Lmhpc3QvZGF0YS9zY1JOQXNlcS5wcm9jZXNzZWQuUmRhdGEiKSkKfQpgYGAKCiMgU2ltcGxlIFBDQXMgdG8gYmVnaW4KCmBgYHtyfQpmb3IoaSBpbiAxOmxlbmd0aChkYXRhKSl7CiAgeSA8LSBkYXRhW1tpXV0kaHVtYW4KICBwY2EgPC0gcHJjb21wKHQoeVtvcmRlcigtMSphcHBseSh5LDEsdmFyKSksXVsxOjEwMDAsXSksc2NhbGU9VCkKICBwY2EgPC0gY2JpbmQoZGF0YVtbaV1dJG1ldGEsQ0RSPWNvbFN1bXMoeT4wKSxwY2EkeCkKICBwcmludChnZ3BhaXJzKHBjYSwgY29sdW1ucz1jKCdQQzEnLCAnUEMyJywgJ1BDMycsICdQQzQnLCAnQ0RSJyksCiAgICAgICAgICBtYXBwaW5nPWFlcyhjb2xvcj1UcmVhdG1lbnQpLCB1cHBlcj1saXN0KGNvbnRpbnVvdXM9J2NvcicpKSkgICAKfQpgYGAKClRoZSBDRFIgY2xlYXJseSBjb3JyZWxhdGVzIHN0cm9uZ2xlIHRvIHRoZSBlaXRoZXIgb3IgYm90aCB0aGUgMXN0IGFuZC9vciAybmQgcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDcykuIFRoaXMgbWFrZXMgaXQgY2xlYXIgdGhhdCB0aGUgQ0RSIHNob3VsZCBhbHdheXMgYmUgY29uc2lkZXJlZCB3aGVuIHBlcmZvcm1pbmcgYW55IG1vZGVsbGluZy4gTGV0J3MgdXMgc2VlIHRoZSBwcm9wb3Rpb24gb2YgdmFyaWFuY2UgYWNjb3VudGVkIGZvciBieSB0aGVzZSBmaXJzdCBQQ3MuIAoKYGBge3J9CmxhcHBseShkYXRhLGZ1bmN0aW9uKHgpewogIHkgPC0geCRodW1hbgogIHBjYSA8LSBwcmNvbXAodCh5W29yZGVyKC0xKmFwcGx5KHksMSx2YXIpKSxdWzE6MTAwMCxdKSxzY2FsZT1UKQogIGVpZ3MgPC0gcGNhJHNkZXZeMgogIHJiaW5kKCJQcm9wLiBvZiBWYXIuIj1laWdzWzE6NV0gLyBzdW0oZWlncyksIkN1bS4gU3VtLiBQcm9wLiBvZiBWYXIuIj1jdW1zdW0oZWlnc1sxOjVdKS9zdW0oZWlncykpCn0pCmBgYAoKCgpgYGB7cn0KZm9yKGkgaW4gMTpsZW5ndGgoZGF0YSkpewogIHkgPC0gY2JpbmQoZGF0YVtbaV1dJG1ldGEsQ0RSPWNvbFN1bXMoZGF0YVtbaV1dJGh1bWFuID4gMCkpCiAgaWYoIlJlc3BvbnNlIiAlaW4lIG5hbWVzKHkpKXsKICAgIHByaW50KGdncGxvdCh5KSArIGFlcyh4PVJlc3BvbnNlLHk9Q0RSLGZpbGw9UmVzcG9uc2UsZ3JvdXA9U2FtcGxlKSArIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYyguMjUsLjUsLjc1KSkpCiAgfSBlbHNlIHsKICAgIHByaW50KGdncGxvdCh5KSArIGFlcyh4PVRyZWF0bWVudCx5PUNEUixmaWxsPVRyZWF0bWVudCxncm91cD1TYW1wbGUpICsgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKC4yNSwuNSwuNzUpKSkKICB9Cn0KYGBgCgpJdCBpcyBjbGVhciB0aGF0IHRoZSBDRFIgaGFzIGJlZW4gZHJpdmluZyBhIGxvdCBvZiBvdXIgb2JzZXJ2YXRpb25zLiBXaGV0aGVyIHRoaXMgaXMgYmlvbG9naWNhbCBpcyBzZXBlcmF0ZSBxdWVzdGlvbiwgYnV0IHJlZ2FyZGxlc3Mgd2Ugc2hvdWxkIGluY2x1ZGUgaXQgaW4gb3VyIG1vZGVsbGluZyBhdHRlbXB0cy4gTGV0IHVzIHBlcmZvcm0gdGhlIERFIGFuYWx5c2lzIHVzaW5nIGVkZ2VSLCBhcyBkb25lIGJ5IE1pa2UsIGJ1dCB0aGlzIHRpbWUgaW5jbHVkaW5nIHRoZSBDRFIuCgojIERFIEFuYWx5c2lzCgojIyBOaWF2ZSBNb2RlbCAobm8gQ0RSKQoKVGhpcyBpcyB0aGUgb3JnaW5hbCBtb2RlbCB1c2VkIGJ5IE1pa2UuIEZvciB0aGlzIGluaXRpYWwgdGVzdCwgd2Ugd2lsbCBvbmx5IHVzZSB0aGUgVkhJTzA5OCBkYXRhc2V0LiBGb3IgcmVmZXJlbmNlLCBNaWtlJ3MgYW5hbHlzaXMgcmV0dXJuZWQ6CgpUcmVhdG1lbnRUcmVhdGVkIHZzLiBUcmVhdG1lbnRVbnRyZWF0ZWQKLTEJMTM5OAowCTk4NTQKMQkyMjEwCgpgYGB7cn0KZGVzaWduLm1hdCA8LSBtb2RlbC5tYXRyaXgofiAwICsgVHJlYXRtZW50ICsgU2FtcGxlLCBkYXRhPWRhdGEkVkhJMDk4JG1ldGEpCmNvbnRyYXN0Lm1hdCA8LSAgbWFrZUNvbnRyYXN0cyhUcmVhdG1lbnRUcmVhdGVkLVRyZWF0bWVudFVudHJlYXRlZCwgbGV2ZWxzPWRlc2lnbi5tYXQpCnZmaXQgPC0gbG1GaXQoZGF0YSRWSEkwOTgkaHVtYW4sIGRlc2lnbi5tYXQpCnZmaXQgPC0gY29udHJhc3RzLmZpdCh2Zml0LCBjb250cmFzdHM9Y29udHJhc3QubWF0KQplZml0IDwtIGVCYXllcyh2Zml0KQpERSA8LSBhcy5kYXRhLnRhYmxlKHRvcFRhYmxlKGVmaXQscC52YWx1ZSA9IDAuMDUsIG49SW5mKSxrZWVwLnJvd25hbWVzPVRSVUUpCkRFWyxkaXJlY3Rpb246PWZhY3Rvcihsb2dGQz4wKV0KbGV2ZWxzKERFJGRpcmVjdGlvbikgPC0gYygiZG93bi5yZWciLCJ1cC5yZWciKQoKbW9kZWxzIDwtIGxpc3QoKQptb2RlbHMkbmlhdmUgPC0gREUKCkRFWywuTixkaXJlY3Rpb25dCmBgYAogCkFzIGV4cGVjdGVkLCB0aGlzIGlzIHRoZSBzYW1lIGFzIHdoYXQgTWlrZSBvYnRhaW5lZC4KCiMjIE5pYXZlIG1vZGVsIHdpdGggQ0RSCgpMZXQncyBmaXJzdCBsb29rIGF0IHRoZSByZWxhdGl2ZSBBSUNzIHdoZW4gYWRkaW5nIHRoaXMgZmFjdG9yLgoKYGBge3J9CmFpYyA8LSB3aXRoKGNiaW5kKGRhdGEkVkhJMDk4JG1ldGEsQ0RSPWNvbFN1bXMoZGF0YSRWSEkwOTgkaHVtYW4+MCkpLAogICAgICAgICAgICBzZWxlY3RNb2RlbChkYXRhJFZISTA5OCRodW1hbiwgbGlzdCgKICAgICAgICAgICAgICBtb2RlbC5tYXRyaXgofiAwICsgVHJlYXRtZW50ICsgU2FtcGxlKSwKICAgICAgICAgICAgICBtb2RlbC5tYXRyaXgofiAwICsgVHJlYXRtZW50ICsgU2FtcGxlICsgQ0RSKQogICAgICAgICAgICApLCBjcml0ZXJpb249ImFpYyIpKQp0YWJsZShhaWMkcHJlZikKdGFibGUoZXhwKGFzLm51bWVyaWMoZGlmZih0KGFpYyRJQ1thaWMkcHJlZj09IjIiLF0pKSkvMikgPCAwLjA1KSAjRkRSIGNvcnJlY3Rpb24gbmVlZGVkPwpgYGAKClRoZSBBSUMgc2hvd3MgdGhhdCB0aGUgbWFqb3JpdHkgb2YgZ2VuZXMgYXJlIGZpdHRlZCBiZXR0ZXIgYnkgdGhlIG1vZGVsIHRoYXQgaW5jb3Jwb3JhdGVzIHRoZSBDRFIuIEZ1cnRoZXJtb3JlLCB0aGUgcHJvcGFiaWxpdGllcyB0aGF0IHRoaXMgbW9kZWwgaXMgYW4gaW1wcm92ZW1lbnQgZm9yIHRoaXMgInN1YnNldCIgbGVhdmVzIG5vIGRvdWJ0cyBhcyB0byB1c2luZyBpdC4KCmBgYHtyfQpkZXNpZ24ubWF0IDwtIG1vZGVsLm1hdHJpeCh+IDAgKyBUcmVhdG1lbnQgKyBTYW1wbGUgKyBDRFIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9Y2JpbmQoZGF0YSRWSEkwOTgkbWV0YSxDRFI9Y29sU3VtcyhkYXRhJFZISTA5OCRodW1hbj4wKSkpCmNvbnRyYXN0Lm1hdCA8LSAgbWFrZUNvbnRyYXN0cyhUcmVhdG1lbnRUcmVhdGVkLVRyZWF0bWVudFVudHJlYXRlZCwgbGV2ZWxzPWRlc2lnbi5tYXQpCnZmaXQgPC0gbG1GaXQoZGF0YSRWSEkwOTgkaHVtYW4sIGRlc2lnbi5tYXQpCnZmaXQgPC0gY29udHJhc3RzLmZpdCh2Zml0LCBjb250cmFzdHM9Y29udHJhc3QubWF0KQplZml0IDwtIGVCYXllcyh2Zml0KQpERSA8LSBhcy5kYXRhLnRhYmxlKHRvcFRhYmxlKGVmaXQscC52YWx1ZSA9IDAuMDUsIG49SW5mKSxrZWVwLnJvd25hbWVzPVRSVUUpCkRFWyxkaXJlY3Rpb246PWZhY3Rvcihsb2dGQz4wKV0KbGV2ZWxzKERFJGRpcmVjdGlvbikgPC0gYygiZG93bi5yZWciLCJ1cC5yZWciKQptb2RlbHMkbmlhdmUuQ0RSIDwtIERFCkRFWywuTixkaXJlY3Rpb25dCmBgYAoKV2Ugc2VlIGEgbGFyZ2UgcmVkdWN0aW9uIGluIHRoZSBudW1iZXIgb2YgdXAucmVnIGdlbmVzIHdoaWNoIGlzIG5vdCBxdWl0ZSBtYXRjaGVkIG1hdGNoZWQgYnkgYW4gaW5jcmVhc2UgaW4gdGhlIG51bWJlciBvZiBkb3duLnJlZyBnZW5lcy4gSW4gdG90YWwsIHRoZSBleHRyYSBjb2VmZmllbmN0IHJlZHVjZXMgdGhlIHRvdGFsIG51bWJlciBvZiBERSBnZW5lcyBieSByb3VnaGx5IDYwMC4gVGhpcyBpcyBhIGdvb2QgaW5kaWNhdGlvbiB0aGF0IGl0IHNob3VsZCBiZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwsIGFzIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpcyBzdWNoIHRoYXQgYXJlIHdlIGFyZSBmYWlybHkgc2FmZSBmcm9tIG92ZXJmaXR0aW5nLiBXaGF0IGlzIHRoZSBvdmVybGFwIGJldHdlZW4gdGhlc2UgbW9kZWxzPwoKYGBge3J9CnggPC0gbWVyZ2UobW9kZWxzJG5pYXZlWywuKHJuLElELG5pYXZlPWRpcmVjdGlvbildLAogICAgICBtb2RlbHMkbmlhdmUuQ0RSWywuKHJuLElELG5pYXZlLkNEUj1kaXJlY3Rpb24pXSwKICAgICAgYnk9Yygicm4iLCJJRCIpLGFsbD1UUlVFLCkKbGV2ZWxzKHgkbmlhdmUpIDwtIGMoLTEsMSwwKQpsZXZlbHMoeCRuaWF2ZS5DRFIpIDwtIGMoLTEsMSwwKQp4W2lzLm5hKHgpXSA8LSAwCnZlbm5EaWFncmFtKHhbLC4obmlhdmUsbmlhdmUuQ0RSKV0sY2V4PTAuOCxpbmNsdWRlPSJ1cCIsbWFpbj0iVXAucmVnLkRFIikKdmVubkRpYWdyYW0oeFssLihuaWF2ZSxuaWF2ZS5DRFIpXSxjZXg9MC44LGluY2x1ZGU9ImRvd24iLG1haW49IkRvd24ucmVnLkRFIikKYGBgCgojIyBMb2dpc3RpYyBNb2RlbAoKRm9yIHRoaXMgbW9kZWwsIGxldCB1cyBtb2RlbCB0aGUgZXhwcmVzc2lvbiBhcyBlaXRoZXIgb24gKCE9MCkgb3Igb2ZmICg9PTApLiBUaGVuIGxldCdzIHVzZSBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIG1vZGVsIHRoZSB0cmVhdG1lbnQgZWZmZWN0LiAKCmBgYHtyfQpkYXRhLmJpbmFyeSA8LSBhcy5kYXRhLnRhYmxlKGRhdGEkVkhJMDk4JGh1bWFuICE9IDApCmRhdGEuYmluYXJ5Wyxyb3cuaWQ6PS5JXQpERSA8LSB3aXRoKGRhdGEkVkhJMDk4JG1ldGEsCiAgICAgZGF0YS5iaW5hcnlbLCBhcy5saXN0KHN1bW1hcnkoZ2xtKHVubGlzdCguU0QpflRyZWF0bWVudCtTYW1wbGUrY2RyKSkkY29lZmZpY2llbnRzWzIsXSksIHJvdy5pZF0KICAgICApCkRFJHJvdy5pZCA8LSByb3duYW1lcyhkYXRhJFZISTA5OCRodW1hbikKREVbLGZkcjo9cC5hZGp1c3QoYFByKD58dHwpYCwgJ2ZkcicpXQpERSA8LSBERVtmZHI8MC4wNV0KREVbLGRpcmVjdGlvbjo9ZmFjdG9yKEVzdGltYXRlPjApXQpsZXZlbHMoREUkZGlyZWN0aW9uKSA8LSBjKCJkb3duLnJlZyIsInVwLnJlZyIpCm1vZGVscyRsb2dpc3RpYyA8LSBERQpERVssLk4sZGlyZWN0aW9uXQpgYGAKCiMjIFplcm8taW5mbGF0ZWQgbW9kZWwKCldlIG5lZWQgdG8gZmlyc3QgZGV0ZXJtaW5lIGFuIGFwcHJvcGlhdGUgdGhyZXNob2xkIHRvIGJlbG93IHdoaWNoIHdlIHNldCB0aGUgdmFsdWUgdG8gemVyby4KCmBgYHtyfQp0aHJlcyA8LSB0aHJlc2hvbGRTQ1JOQUNvdW50TWF0cml4KGRhdGEkVkhJMDk4JGh1bWFuLCBuYmlucyA9IDIwLCBtaW5fcGVyX2JpbiA9IDMwKQpwYXIobWZyb3c9Yyg1LDQpKQpwcmludChwbG90KHRocmVzKSkKYGBgCgpNaWtlJ3Mgbm9ybWFsaXNhdGlvbiBoYXMgYXBwYXJlbnRseSBhbHJlYWR5IGRvbmUgdGhpcyBhbmQgYXQgYWxsIG1lZGlhbiBleHByZXNzaW9uIGJpbm5pbmdzLCB0aGUgdGhyZXNob2xkIGlzIHJlcG9ydGVkIGFzIHplcm8uCgpgYGB7cn0KZXhwTWF0IDwtIGFzLm1hdHJpeChkYXRhJFZISTA5OCRodW1hbikKcm93bmFtZXMoZXhwTWF0KSA8LSBtYWtlLm5hbWVzKHJvd25hbWVzKGV4cE1hdCksdW5pcXVlID0gVCkKY0RhdGEgPC0gYXMuZGF0YS5mcmFtZShjYmluZChkYXRhJFZISTA5OCRtZXRhLENEUj1jb2xTdW1zKGRhdGEkVkhJMDk4JGh1bWFuPjApKSkKcm93bmFtZXMoY0RhdGEpIDwtIGNvbG5hbWVzKGV4cE1hdCkKY0RhdGEkVHJlYXRtZW50IDwtIGZhY3RvcihjRGF0YSRUcmVhdG1lbnQsbGV2ZWxzID0gYygiVW50cmVhdGVkIiwiVHJlYXRlZCIpKQpmRGF0YSA8LSBkYXRhLmZyYW1lKGdlbmU9cm93bmFtZXMoZXhwTWF0KSkKcm93bmFtZXMoZkRhdGEpIDwtIHJvd25hbWVzKGV4cE1hdCkKeCA8LSBGcm9tTWF0cml4KGV4cE1hdCxjRGF0YSxmRGF0YSkKCnkgPC0gemxtKH4gVHJlYXRtZW50ICsgU2FtcGxlICsgQ0RSLCB4KQp6IDwtIHN1bW1hcnkoeSxkb0xSVD0iVHJlYXRtZW50VHJlYXRlZCIpCnogPC0geiRkYXRhdGFibGUKYGBgCgpgYGB7cn0KREUgPC0gbWVyZ2UoCiAgeltjb250cmFzdD09J1RyZWF0bWVudFRyZWF0ZWQnICYgY29tcG9uZW50PT0nSCcsLihwcmltZXJpZCwgYFByKD5DaGlzcSlgKV0sCiAgeltjb250cmFzdD09J1RyZWF0bWVudFRyZWF0ZWQnICYgY29tcG9uZW50PT0nbG9nRkMnLCAuKHByaW1lcmlkLCBjb2VmLCBjaS5oaSwgY2kubG8pXSwKYnk9J3ByaW1lcmlkJykKREVbLGZkcjo9cC5hZGp1c3QoYFByKD5DaGlzcSlgLCAnZmRyJyldCkRFIDwtIERFW2ZkcjwwLjA1XQpERVssZGlyZWN0aW9uOj1mYWN0b3IoY29lZj4wKV0KbGV2ZWxzKERFJGRpcmVjdGlvbikgPC0gYygiZG93bi5yZWciLCJ1cC5yZWciKQptb2RlbHMkemVyby5pbmYgPC0gREUKREVbLC5OLGRpcmVjdGlvbl0KYGBgCgpOb3RlIHRoYXQgdGhlIGFib3ZlIG1vZGVsIGNhbGxzIHNvbWV0aGluZyBhcyBERSBpZiB0aGUgaHVyZGxlIChnZW5lIGJlaW5nIG9uIG9yIG9mZikgaXMgc2lnbmlmZ2FudGx5IGFsdGVyZWQgdy5yLnQuIHRvIHRoZSBjb250cmFzdCB0ZXN0ZWQuIFRoZXJlIGRvZXMgbm90IGhhdmUgdG8gYmUgYSBjaGFuZ2UgaW4gdGhlIGV4cHJlc3Npb24gb2YgZ2VuZXMgdGhhdCBhcmUgIm9uIi4KCldoYXQgaXMgdGhlIG92ZXJsYXAgdy5yLnQuIHRoZSBvdGhlciBtb2RlbHM/CgpgYGB7cn0KeCA8LSBtZXJnZShtb2RlbHMkbmlhdmVbLC4ocm4sSUQsbmlhdmU9ZGlyZWN0aW9uKV0sCiAgICAgIG1vZGVscyRuaWF2ZS5DRFJbLC4ocm4sSUQsbmlhdmUuQ0RSPWRpcmVjdGlvbildLAogICAgICBieT1jKCJybiIsIklEIiksYWxsPVRSVUUsKQp4IDwtIG1lcmdlKHgsCiAgICAgICAgICAgbW9kZWxzJHplcm8uaW5mWywuKHByaW1lcmlkLHplcm8uaW5mPWRpcmVjdGlvbildLAogICAgICAgICAgIGJ5Lng9YygiSUQiKSxieS55PWMoInByaW1lcmlkIiksYWxsPVRSVUUpCnggPC0gbWVyZ2UoeCwKICAgICAgICAgICBtb2RlbHMkbG9naXN0aWNbLC4ocm93LmlkLGxvZ2lzdGljPWRpcmVjdGlvbildLAogICAgICAgICAgIGJ5Lng9YygiSUQiKSxieS55PWMoInJvdy5pZCIpLGFsbD1UUlVFKQpsZXZlbHMoeCRuaWF2ZSkgPC0gYygtMSwxLDApCmxldmVscyh4JG5pYXZlLkNEUikgPC0gYygtMSwxLDApCmxldmVscyh4JHplcm8uaW5mKSA8LSBjKC0xLDEsMCkKbGV2ZWxzKHgkbG9naXN0aWMpIDwtIGMoLTEsMSwwKQp4W2lzLm5hKHgpXSA8LSAwCnZlbm5EaWFncmFtKHhbLC4obmlhdmUsbmlhdmUuQ0RSLHplcm8uaW5mLGxvZ2lzdGljKV0sY2V4PTAuOCxpbmNsdWRlPSJ1cCIsbWFpbj0iVXAucmVnLkRFIikKdmVubkRpYWdyYW0oeFssLihuaWF2ZSxuaWF2ZS5DRFIsemVyby5pbmYsbG9naXN0aWMpXSxjZXg9MC44LGluY2x1ZGU9ImRvd24iLG1haW49IkRvd24ucmVnLkRFIikKYGBgCgpBbmQgbGV0J3Mgc2VlIHdoYXQgaGFwcGVuZHMgd2hlbiB3ZSBlbmZvcmNlIGEgTEZDIHRocmVzaG9sZC4KCmBgYHtyfQp4IDwtIG1lcmdlKG1vZGVscyRuaWF2ZVthYnMobG9nRkMpPi41LC4ocm4sSUQsbmlhdmU9ZGlyZWN0aW9uKV0sCiAgICAgIG1vZGVscyRuaWF2ZS5DRFJbYWJzKGxvZ0ZDKT4uNSwuKHJuLElELG5pYXZlLkNEUj1kaXJlY3Rpb24pXSwKICAgICAgYnk9Yygicm4iLCJJRCIpLGFsbD1UUlVFLCkKeCA8LSBtZXJnZSh4LAogICAgICAgICAgIG1vZGVscyR6ZXJvLmluZlthYnMoY29lZik+LjUsLihwcmltZXJpZCx6ZXJvLmluZj1kaXJlY3Rpb24pXSwKICAgICAgICAgICBieS54PWMoIklEIiksYnkueT1jKCJwcmltZXJpZCIpLGFsbD1UUlVFKQpsZXZlbHMoeCRuaWF2ZSkgPC0gYygtMSwxLDApCmxldmVscyh4JG5pYXZlLkNEUikgPC0gYygtMSwxLDApCmxldmVscyh4JHplcm8uaW5mKSA8LSBjKC0xLDEsMCkKeFtpcy5uYSh4KV0gPC0gMAp2ZW5uRGlhZ3JhbSh4WywuKG5pYXZlLG5pYXZlLkNEUix6ZXJvLmluZildLGNleD0wLjgsaW5jbHVkZT0idXAiLG1haW49IlVwLnJlZy5ERSIpCnZlbm5EaWFncmFtKHhbLC4obmlhdmUsbmlhdmUuQ0RSLHplcm8uaW5mKV0sY2V4PTAuOCxpbmNsdWRlPSJkb3duIixtYWluPSJEb3duLnJlZy5ERSIpCmBgYAoKI0RFIGdlbmVzIHBsb3RzCgpGaW5hbGx5LCBsZXQncyBtYWtlIGEgZmV3IHByZXR0eSBwbG90cyBvZiB0aGUgbW9zdCBERSBnZW5lcyBmb3IgZWFjaCBtb2RlbC4KCiMjIE5pYXZlIG1vZGVsCgpgYGB7cn0KREUgPC0gbW9kZWxzJG5pYXZlCmkgPC0gd2hpY2gocm93bmFtZXMoZGF0YSRWSEkwOTgkaHVtYW4pICVpbiUgREVbb3JkZXIoYWRqLlAuVmFsKV1bYWJzKGxvZ0ZDKT4uNV1bMToxNixJRF0pCnggPC0gbWVyZ2UobWVsdChkYXRhJFZISTA5OCRodW1hbltpLF0pLGRhdGEkVkhJMDk4JG1ldGEsYnkueD1jKCJWYXIyIiksYnkueT1jKCJzdWIuc2FtcGxlIikpCmdncGxvdCh4KSArIGFlcyh4PVRyZWF0bWVudCx5PXZhbHVlLGdyb3VwPVNhbXBsZSxmaWxsPVRyZWF0bWVudCkgKyBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoLjUpKSArIGZhY2V0X3dyYXAoflZhcjEsc2NhbGU9J2ZyZWVfeScpCmBgYAoKYGBge3J9CkRFIDwtIG1vZGVscyRuaWF2ZQppIDwtIHdoaWNoKHJvd25hbWVzKGRhdGEkVkhJMDk4JGh1bWFuKSAlaW4lIERFW29yZGVyKGFkai5QLlZhbCldW2Ficyhsb2dGQyk+LjVdWzE6NTAsSURdKQpqIDwtIGRhdGEkVkhJMDk4JG1ldGFbLG9yZGVyKFRyZWF0bWVudCldCmxhYmVscy5jb2wgPC0gcmVwKCIiLGxlbmd0aChpKSkKYW5uLmNvbCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkVkhJMDk4JG1ldGFbaiwuKFNhbXBsZSxUcmVhdG1lbnQpXSkKcm93bmFtZXMoYW5uLmNvbCkgPC0gZGF0YSRWSEkwOTgkbWV0YVtqLHN1Yi5zYW1wbGVdCnBoZWF0bWFwKGRhdGEkVkhJMDk4JGh1bWFuW2ksal0sbGFiZWxzX2NvbD1sYWJlbHMuY29sLGFubm90YXRpb25fY29sID0gYW5uLmNvbCxjbHVzdGVyX2NvbHMgPSBGKQpgYGAKCiMjIE5pYXZlIENEUiBtb2RlbAoKYGBge3J9CkRFIDwtIG1vZGVscyRuaWF2ZS5DRFIKaSA8LSB3aGljaChyb3duYW1lcyhkYXRhJFZISTA5OCRodW1hbikgJWluJSBERVtvcmRlcihhZGouUC5WYWwpXVthYnMobG9nRkMpPi41XVsxOjE2LElEXSkKeCA8LSBtZXJnZShtZWx0KGRhdGEkVkhJMDk4JGh1bWFuW2ksXSksZGF0YSRWSEkwOTgkbWV0YSxieS54PWMoIlZhcjIiKSxieS55PWMoInN1Yi5zYW1wbGUiKSkKZ2dwbG90KHgpICsgYWVzKHg9VHJlYXRtZW50LHk9dmFsdWUsZ3JvdXA9U2FtcGxlLGZpbGw9VHJlYXRtZW50KSArIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYyguNSkpICsgZmFjZXRfd3JhcCh+VmFyMSxzY2FsZT0nZnJlZV95JykKYGBgCgpgYGB7cn0KREUgPC0gbW9kZWxzJG5pYXZlLkNEUgppIDwtIHdoaWNoKHJvd25hbWVzKGRhdGEkVkhJMDk4JGh1bWFuKSAlaW4lIERFW29yZGVyKGFkai5QLlZhbCldW2Ficyhsb2dGQyk+LjVdWzE6NTAsSURdKQpqIDwtIGRhdGEkVkhJMDk4JG1ldGFbLG9yZGVyKFRyZWF0bWVudCldCmxhYmVscy5jb2wgPC0gcmVwKCIiLGxlbmd0aChpKSkKYW5uLmNvbCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkVkhJMDk4JG1ldGFbaiwuKFNhbXBsZSxUcmVhdG1lbnQpXSkKcm93bmFtZXMoYW5uLmNvbCkgPC0gZGF0YSRWSEkwOTgkbWV0YVtqLHN1Yi5zYW1wbGVdCnBoZWF0bWFwKGRhdGEkVkhJMDk4JGh1bWFuW2ksal0sbGFiZWxzX2NvbD1sYWJlbHMuY29sLGFubm90YXRpb25fY29sID0gYW5uLmNvbCxjbHVzdGVyX2NvbHMgPSBGKQpgYGAKCiMjIFplcm8taW5mbGF0ZWQgbW9kZWwKCmBgYHtyfQpERSA8LSBtb2RlbHMkemVyby5pbmYKaSA8LSB3aGljaChyb3duYW1lcyhkYXRhJFZISTA5OCRodW1hbikgJWluJSBERVtvcmRlcihmZHIpXVthYnMoY29lZik+LjVdWzE6MTYscHJpbWVyaWRdKQp4IDwtIG1lcmdlKG1lbHQoZGF0YSRWSEkwOTgkaHVtYW5baSxdKSxkYXRhJFZISTA5OCRtZXRhLGJ5Lng9YygiVmFyMiIpLGJ5Lnk9Yygic3ViLnNhbXBsZSIpKQpnZ3Bsb3QoeCkgKyBhZXMoeD1UcmVhdG1lbnQseT12YWx1ZSxncm91cD1TYW1wbGUsZmlsbD1UcmVhdG1lbnQpICsgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKC41KSkgKyBmYWNldF93cmFwKH5WYXIxLHNjYWxlPSdmcmVlX3knKQpgYGAKCmBgYHtyfQpERSA8LSBtb2RlbHMkemVyby5pbmYKaSA8LSB3aGljaChyb3duYW1lcyhkYXRhJFZISTA5OCRodW1hbikgJWluJSBERVtvcmRlcihmZHIpXVthYnMoY29lZik+LjVdWzE6NTAscHJpbWVyaWRdKQpqIDwtIGRhdGEkVkhJMDk4JG1ldGFbLG9yZGVyKFRyZWF0bWVudCldCmxhYmVscy5jb2wgPC0gcmVwKCIiLGxlbmd0aChpKSkKYW5uLmNvbCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkVkhJMDk4JG1ldGFbaiwuKFNhbXBsZSxUcmVhdG1lbnQpXSkKcm93bmFtZXMoYW5uLmNvbCkgPC0gZGF0YSRWSEkwOTgkbWV0YVtqLHN1Yi5zYW1wbGVdCnBoZWF0bWFwKGRhdGEkVkhJMDk4JGh1bWFuW2ksal0sbGFiZWxzX2NvbD1sYWJlbHMuY29sLGFubm90YXRpb25fY29sID0gYW5uLmNvbCxjbHVzdGVyX2NvbHMgPSBGKQpgYGAK